﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace OF.Utility
{
    /// <summary>
    /// 该类可以用作提供数据的公共方法类
    /// </summary>
    public abstract class SQLServerProvider
    {
        


        #region common

        /// <summary>
        /// 获取记录集总数
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="sqlstr"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        private static int GetRowCount(string conn, string sqlstr, SqlParameter[] param)
        {
            string sqlcmdCnt = string.Format("select Count (1) from ({0}) as t", sqlstr);
            return (int)SQLHelper.ExecuteScalar(conn, CommandType.Text, sqlcmdCnt, param);
        }

        /// <summary>
        /// 返回可以应用分页的Reader,仅试用于2005以上版本的库
        /// </summary>
        /// <param name="conn">连接字符串</param>
        /// <param name="sqlstr">查询字符串</param>
        /// <param name="param">参数</param>
        /// <param name="primarykey">不允许为空</param>
        /// <param name="orderby">如果传递空字符串,将会以主键排序</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页大小</param>
        /// <returns>T类型</returns>
        private static SqlDataReader GetData(string conn, string sqlstr, SqlParameter[] param, string primarykey, string orderby, int pageIndex, int pageSize)
        {
            int PageIndex = pageIndex < 1 ? 1 : pageIndex;
            //确保RowNumber不会为空,层级为:用户输入的ORDER-->主键-->不排序
            string orderbystr = string.Format("ORDER BY {0}", string.IsNullOrEmpty(orderby) ? primarykey : orderby);
            //用户SQL 2005 的分页
            string strnewsql = string.Format("select top {1} * from (select *,Row_Number() over({0}) as num  from ({3} )as t)as t2 where num>{2} ", orderbystr, pageSize, ((pageIndex - 1) * pageSize), sqlstr);
            return SQLHelper.ExecuteReader(conn, CommandType.Text, strnewsql, param);
        }
        #endregion

        #region 适合手动创建的无构造函数的实体

        /// <summary>
        /// 创建对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dr"></param>
        /// <returns></returns>
        public static T Create<T>(IDataRecord dr)
        {
            Type modelType = typeof(T);
            T model;

            if (modelType.IsValueType || modelType.ToString() == "System.String") //处理值类型和string
            {
                return (T)dr[0];
            }

            model = Activator.CreateInstance<T>();

            for (int i = 0; i < dr.FieldCount; i++)
            {
                if (dr.GetValue(i) == DBNull.Value) continue;
                PropertyInfo pi = modelType.GetProperty(dr.GetName(i), BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
                if (pi == null) continue; //实体对象中没有这个属性

                if (pi.PropertyType.IsEnum)//枚举
                {
                    pi.SetValue(model, Enum.Parse(pi.PropertyType, dr.GetValue(i).ToString()), null);
                    continue;
                }

                Type type = pi.PropertyType;

                //特殊类型,可空类型
                if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    type = Nullable.GetUnderlyingType(pi.PropertyType);

                pi.SetValue(model, Convert.ChangeType(dr.GetValue(i), type), null);
            }
            return model;
        }

        
        /// <summary>
        /// 通过DataRow创建实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="row"></param>
        /// <returns></returns>
        public static T Create<T>(DataRow row)
        {
            Type modelType = typeof(T);
            T model;
            DataTable tb = row.Table;
            var columns = tb.Columns;
            if (modelType.IsValueType || modelType.ToString() == "System.String") //处理值类型和string
            {
                return (T)row[0];
            }

            model = Activator.CreateInstance<T>();

            for (int i = 0; i < tb.Columns.Count; i++)
            {
                
                if (row[i]== DBNull.Value) continue;
                PropertyInfo pi = modelType.GetProperty(tb.Columns[i].ColumnName, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance);
                if (pi == null) continue; //实体对象中没有这个属性

                if (pi.PropertyType.IsEnum)//枚举
                {
                    pi.SetValue(model, Enum.Parse(pi.PropertyType, row[i].ToString()), null);
                    continue;
                }

                Type type = pi.PropertyType;

                //特殊类型,可空类型
                if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    type = Nullable.GetUnderlyingType(pi.PropertyType);

                pi.SetValue(model, Convert.ChangeType(row[i], type), null);
            }
            return model;
        }


        /// <summary>
        /// 通过DataTable创建实体列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tb"></param>
        /// <returns></returns>
        public static List<T> CreateList<T>(DataTable tb)
        {
            if (tb == null)
            {
                return new List<T>();
            }
            return (from DataRow row in tb.Rows select Create<T>(row)).ToList();
        }

        /// <summary>
        /// 是否包含记录
        /// </summary>
        /// <param name="conn"></param>
        /// <param name="sqlstr"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static bool HasRows(string conn, string sqlstr, params SqlParameter[] param)
        {
            using (SqlDataReader dr = SQLHelper.ExecuteReader(conn, CommandType.Text, sqlstr, param))
            {
                return dr.HasRows;
            }
        }
        /// <summary>
        /// 无类型约束创建对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="conn"></param>
        /// <param name="sqlstr"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static T CreateObject<T>(string conn, string sqlstr, params SqlParameter[] param)
        {
            using (SqlDataReader dr = SQLHelper.ExecuteReader(conn, CommandType.Text, sqlstr, param))
            {
                if (dr.Read())
                {
                    return Create<T>(dr);
                }
            }
            return default(T);
        }
        #region 根据断言获取对象及列表信息
        /// <summary>
        /// 根据断言获取对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="conn"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static T CreateObject<T>(string conn, Expression<Func<T, bool>> predicate)
        {
            if (!conn.Contains("="))
            {
                conn = ConfigurationManager.ConnectionStrings[conn].ConnectionString;
            }
            Type modelType = typeof(T);
            var tableName = modelType.Name;
            if (tableName.Right(4) == "Info")
            {
                tableName = tableName.Substring(0, tableName.Length - 4);
            }
            var translator = new QueryTranslator();
            string whereClause = translator.Translate<T>(predicate);
            string szSql = $"SELECT top 1 * FROM {tableName} WHERE ";
            if (string.IsNullOrEmpty(whereClause))
            {
                whereClause = "1=1";
            }
            szSql = szSql + whereClause;
            using (SqlDataReader dr = SQLHelper.ExecuteReader(conn, CommandType.Text, szSql))
            {
                if (dr.Read())
                {
                    return Create<T>(dr);
                }
            }
            return default(T);
        }
        /// <summary>
        /// 根据断言获取对象列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="conn"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static List<T> CreateList<T>(string conn, Expression<Func<T, bool>> predicate)
        {
            if (!conn.Contains("="))
            {
                conn = ConfigurationManager.ConnectionStrings[conn].ConnectionString;
            }
            List<T> list = new List<T>();
            Type modelType = typeof(T);
            var tableName = modelType.Name;
            if (tableName.Right(4) == "Info")
            {
                tableName = tableName.Substring(0, tableName.Length - 4);
            }
            var translator = new QueryTranslator();
            string whereClause = translator.Translate<T>(predicate);
            string szSql = $"SELECT * FROM {tableName} WHERE ";
            if (string.IsNullOrEmpty(whereClause))
            {
                whereClause = "1=1";
            }
            if (whereClause == "1")
            {
                whereClause = "1=1";
            }
            szSql = szSql + whereClause;
            using (SqlDataReader dr = SQLHelper.ExecuteReader(conn, CommandType.Text, szSql))
            {
                while (dr.Read())
                {
                    T newt = Create<T>(dr);
                    if (newt != null)
                        list.Add(newt);
                }
            }
            return list;
        }

        #endregion



        /// <summary>
        /// 无类型约束创建列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="conn"></param>
        /// <param name="sqlstr"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static List<T> CreateList<T>(string conn, string sqlstr, params SqlParameter[] param)
        {
            List<T> list = new List<T>();
            using (SqlDataReader dr = SQLHelper.ExecuteReader(conn, CommandType.Text, sqlstr, param))
            {
                while (dr.Read())
                {
                    T newt = Create<T>(dr);
                    if (newt != null)
                        list.Add(newt);
                }
            }
            return list;
        }

        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="conn"></param>
        /// <param name="cmdType"></param>
        /// <param name="sqlStr"></param>
        /// <param name="param"></param>
        /// <returns></returns>
        public static List<T> CreateList<T>(string conn, CommandType cmdType,string sqlStr, params SqlParameter[] param)
        {
            List<T> list = new List<T>();
            using (SqlDataReader dr = SQLHelper.ExecuteReader(conn,cmdType, sqlStr, param))
            {
                while (dr.Read())
                {
                    T newt = Create<T>(dr);
                    if (newt != null)
                        list.Add(newt);
                }
            }
            return list;
        }

        /// <summary>
        /// 无类型约束分页
        /// </summary>
        /// <typeparam name="T">返回值</typeparam>
        /// <param name="conn">连接字符串</param>
        /// <param name="sqlstr">查询字符串</param>
        /// <param name="param">参数数组</param>
        /// <param name="primarykey">主键,主键与排序字段至少要填写一项</param>
        /// <param name="orderby">排序字段,如果需要降序 后边跟 DESC,此项为空将默认按照主键排序</param>
        /// <param name="pageIndex">页码,从1开始</param>
        /// <param name="pageSize">页容量</param>
        /// <returns>T类型</returns>
        /// <exception cref="ArgumentException">查询语句末尾不能包含Order By 否则会引发异常</exception>
        public static PageOfList<T> CreatePage<T>(string conn, string sqlstr, SqlParameter[] param, string primarykey, string orderby, int pageIndex, int pageSize)
        {
            List<T> t = new List<T>();
            using (SqlDataReader dr = GetData(conn, sqlstr, param, primarykey, orderby, pageIndex, pageSize))
            {
                while (dr.Read())
                {
                    T newt = Create<T>(dr);
                    if (newt != null) 
                        t.Add(newt);
                }
            }
            return new PageOfList<T>(t, pageIndex, pageSize, GetRowCount(conn, sqlstr, param));
        }
        #endregion


        public static int GetMaxID(string connectionString,string FieldName, string TableName)
        {
            string strsql = "select max(" + FieldName + ")+1 from " + TableName;
            object obj = GetSingle(connectionString, strsql);
            if (obj == null)
            {
                return 1;
            }
            else
            {
                return int.Parse(obj.ToString());
            }
        }

        public static object GetSingle(string connectionString,string SQLString)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand(SQLString, connection))
                {
                    try
                    {
                        connection.Open();
                        object obj = cmd.ExecuteScalar();
                        if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
                        {
                            return null;
                        }
                        else
                        {
                            return obj;
                        }
                    }
                    catch (System.Data.SqlClient.SqlException e)
                    {
                        connection.Close();
                        throw e;
                    }
                }
            }
        }
        public static object GetSingle(string connectionString, string SQLString, int Times)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                using (SqlCommand cmd = new SqlCommand(SQLString, connection))
                {
                    try
                    {
                        connection.Open();
                        cmd.CommandTimeout = Times;
                        object obj = cmd.ExecuteScalar();
                        if ((Object.Equals(obj, null)) || (Object.Equals(obj, System.DBNull.Value)))
                        {
                            return null;
                        }
                        else
                        {
                            return obj;
                        }
                    }
                    catch (System.Data.SqlClient.SqlException e)
                    {
                        connection.Close();
                        throw e;
                    }
                }
            }
        }
    }

    public class QueryTranslator : ExpressionVisitor
    {
        private StringBuilder sb;
        private string _orderBy = string.Empty;
        private int? _skip = null;
        private int? _take = null;
        private string _whereClause = string.Empty;

        public int? Skip
        {
            get
            {
                return _skip;
            }
        }

        public int? Take
        {
            get
            {
                return _take;
            }
        }

        public string OrderBy
        {
            get
            {
                return _orderBy;
            }
        }

        public string WhereClause
        {
            get
            {
                return _whereClause;
            }
        }

        public QueryTranslator()
        {

        }

        public string Translate<T>(Expression expression)
        {
            this.sb = new StringBuilder();
            this.Visit(expression);
            _whereClause = this.sb.ToString();
            return _whereClause;
        }
        public string Translate<T>(Expression<Func<T, bool>> predicate)
        {
            this.sb = new StringBuilder();
            this.Visit(predicate);
            _whereClause = this.sb.ToString();

            return _whereClause;
        }
        private static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote)
            {
                e = ((UnaryExpression)e).Operand;
            }
            return e;
        }

        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Where")
            {
                this.Visit(m.Arguments[0]);
                LambdaExpression lambda = (LambdaExpression)StripQuotes(m.Arguments[1]);
                this.Visit(lambda.Body);
                return m;
            }
            else if (m.Method.Name == "Take")
            {
                if (this.ParseTakeExpression(m))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }
            else if (m.Method.Name == "Skip")
            {
                if (this.ParseSkipExpression(m))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }
            else if (m.Method.Name == "OrderBy")
            {
                if (this.ParseOrderByExpression(m, "ASC"))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }
            else if (m.Method.Name == "OrderByDescending")
            {
                if (this.ParseOrderByExpression(m, "DESC"))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }

            throw new NotSupportedException(string.Format("The method '{0}' is not supported", m.Method.Name));
        }

        protected override Expression VisitUnary(UnaryExpression u)
        {
            switch (u.NodeType)
            {
                case ExpressionType.Not:
                    sb.Append(" NOT ");
                    this.Visit(u.Operand);
                    break;
                case ExpressionType.Convert:
                    this.Visit(u.Operand);
                    break;
                default:
                    throw new NotSupportedException(string.Format("The unary operator '{0}' is not supported", u.NodeType));
            }
            return u;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression b)
        {
            sb.Append("(");
            this.Visit(b.Left);

            switch (b.NodeType)
            {
                case ExpressionType.And:
                    sb.Append(" AND ");
                    break;

                case ExpressionType.AndAlso:
                    sb.Append(" AND ");
                    break;

                case ExpressionType.Or:
                    sb.Append(" OR ");
                    break;

                case ExpressionType.OrElse:
                    sb.Append(" OR ");
                    break;

                case ExpressionType.Equal:
                    if (IsNullConstant(b.Right))
                    {
                        sb.Append(" IS ");
                    }
                    else
                    {
                        sb.Append(" = ");
                    }
                    break;

                case ExpressionType.NotEqual:
                    if (IsNullConstant(b.Right))
                    {
                        sb.Append(" IS NOT ");
                    }
                    else
                    {
                        sb.Append(" <> ");
                    }
                    break;

                case ExpressionType.LessThan:
                    sb.Append(" < ");
                    break;

                case ExpressionType.LessThanOrEqual:
                    sb.Append(" <= ");
                    break;

                case ExpressionType.GreaterThan:
                    sb.Append(" > ");
                    break;

                case ExpressionType.GreaterThanOrEqual:
                    sb.Append(" >= ");
                    break;

                default:
                    throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", b.NodeType));

            }

            this.Visit(b.Right);
            sb.Append(")");
            return b;
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            IQueryable q = c.Value as IQueryable;

            if (q == null && c.Value == null)
            {
                //sb.Append("NULL");
            }
            else if (q == null)
            {
                switch (Type.GetTypeCode(c.Value.GetType()))
                {
                    case TypeCode.Boolean:
                        sb.Append(((bool)c.Value) ? 1 : 0);
                        break;

                    case TypeCode.String:
                        sb.Append("'");
                        sb.Append(c.Value);
                        sb.Append("'");
                        break;

                    case TypeCode.DateTime:
                        sb.Append("'");
                        //sb.Append(c.Value);
                        sb.Append(Convert.ToDateTime(c.Value).ToString("yyyy-MM-dd HH:mm:ss"));
                        sb.Append("'");
                        break;

                    case TypeCode.Object:
                        throw new NotSupportedException(string.Format("The constant for '{0}' is not supported", c.Value));

                    default:
                        sb.Append(c.Value);
                        break;
                }
            }

            return c;
        }

        protected override Expression VisitMember(MemberExpression m)
        {
            if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter)
            {
                sb.Append(m.Member.Name);
                return m;
            }
            else if (m.Expression != null && m.Expression.NodeType == ExpressionType.Constant)
            {
                // Recurse down to see if we can simplify...
                var expression = m.Expression;
                object container = ((ConstantExpression)expression).Value;
                var member = m.Member;
                if (member is FieldInfo)
                {
                    object value = ((FieldInfo)member).GetValue(container); ;
                    //return Expression.Constant(value);
                    return VisitConstant(Expression.Constant(value));
                }
                if (member is PropertyInfo)
                {
                    object value = ((PropertyInfo)member).GetValue(container, null);
                    //return Expression.Constant(value);
                    return VisitConstant(Expression.Constant(value));
                }
            }
            else if (m.Expression != null && m.Expression.NodeType == ExpressionType.MemberAccess)
            {
                //MemberExpression outerMember = (MemberExpression)m.Expression;
                //PropertyInfo outerProp = (PropertyInfo)outerMember.Member;
                //MemberExpression innerMember = (MemberExpression)outerMember.Expression;
                //FieldInfo innerField = (FieldInfo)innerMember.Member;
                //ConstantExpression ce = (ConstantExpression)innerMember.Expression;
                //object innerObj = ce.Value;
                //object outerObj = innerField.GetValue(innerObj);
                var objectMember = Expression.Convert(m, typeof(object));
                var getterLambda = Expression.Lambda<Func<object>>(objectMember);
                var getter = getterLambda.Compile();
                object value = getter();
                return VisitConstant(Expression.Constant(value));
            }


            throw new NotSupportedException(string.Format("The member '{0}' is not supported", m.Member.Name));
        }

        protected bool IsNullConstant(Expression exp)
        {
            return (exp.NodeType == ExpressionType.Constant && ((ConstantExpression)exp).Value == null);
        }

        private bool ParseOrderByExpression(MethodCallExpression expression, string order)
        {
            UnaryExpression unary = (UnaryExpression)expression.Arguments[1];
            LambdaExpression lambdaExpression = (LambdaExpression)unary.Operand;

            lambdaExpression = (LambdaExpression)Evaluator.PartialEval(lambdaExpression);

            MemberExpression body = lambdaExpression.Body as MemberExpression;
            if (body != null)
            {
                if (string.IsNullOrEmpty(_orderBy))
                {
                    _orderBy = string.Format("{0} {1}", body.Member.Name, order);
                }
                else
                {
                    _orderBy = string.Format("{0}, {1} {2}", _orderBy, body.Member.Name, order);
                }

                return true;
            }

            return false;
        }

        private bool ParseTakeExpression(MethodCallExpression expression)
        {
            ConstantExpression sizeExpression = (ConstantExpression)expression.Arguments[1];

            int size;
            if (int.TryParse(sizeExpression.Value.ToString(), out size))
            {
                _take = size;
                return true;
            }

            return false;
        }

        private bool ParseSkipExpression(MethodCallExpression expression)
        {
            ConstantExpression sizeExpression = (ConstantExpression)expression.Arguments[1];

            int size;
            if (int.TryParse(sizeExpression.Value.ToString(), out size))
            {
                _skip = size;
                return true;
            }

            return false;
        }
    }


    public static class Evaluator
    {

        /// <summary>

        /// Performs evaluation & replacement of independent sub-trees

        /// </summary>

        /// <param name="expression">The root of the expression tree.</param>

        /// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>

        /// <returns>A new tree with sub-trees evaluated and replaced.</returns>

        public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
        {

            return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);

        }



        /// <summary>

        /// Performs evaluation & replacement of independent sub-trees

        /// </summary>

        /// <param name="expression">The root of the expression tree.</param>

        /// <returns>A new tree with sub-trees evaluated and replaced.</returns>

        public static Expression PartialEval(Expression expression)
        {

            return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);

        }



        private static bool CanBeEvaluatedLocally(Expression expression)
        {

            return expression.NodeType != ExpressionType.Parameter;

        }



        /// <summary>

        /// Evaluates & replaces sub-trees when first candidate is reached (top-down)

        /// </summary>

        class SubtreeEvaluator : ExpressionVisitor
        {

            HashSet<Expression> candidates;



            internal SubtreeEvaluator(HashSet<Expression> candidates)
            {

                this.candidates = candidates;

            }



            internal Expression Eval(Expression exp)
            {

                return this.Visit(exp);

            }



            public override Expression Visit(Expression exp)
            {

                if (exp == null)
                {

                    return null;

                }

                if (this.candidates.Contains(exp))
                {

                    return this.Evaluate(exp);

                }

                return base.Visit(exp);

            }



            private Expression Evaluate(Expression e)
            {

                if (e.NodeType == ExpressionType.Constant)
                {

                    return e;

                }

                LambdaExpression lambda = Expression.Lambda(e);

                Delegate fn = lambda.Compile();

                return Expression.Constant(fn.DynamicInvoke(null), e.Type);

            }

        }



        /// <summary>

        /// Performs bottom-up analysis to determine which nodes can possibly

        /// be part of an evaluated sub-tree.

        /// </summary>

        class Nominator : ExpressionVisitor
        {

            Func<Expression, bool> fnCanBeEvaluated;

            HashSet<Expression> candidates;

            bool cannotBeEvaluated;



            internal Nominator(Func<Expression, bool> fnCanBeEvaluated)
            {

                this.fnCanBeEvaluated = fnCanBeEvaluated;

            }



            internal HashSet<Expression> Nominate(Expression expression)
            {

                this.candidates = new HashSet<Expression>();

                this.Visit(expression);

                return this.candidates;

            }



            public override Expression Visit(Expression expression)
            {

                if (expression != null)
                {

                    bool saveCannotBeEvaluated = this.cannotBeEvaluated;

                    this.cannotBeEvaluated = false;

                    base.Visit(expression);

                    if (!this.cannotBeEvaluated)
                    {

                        if (this.fnCanBeEvaluated(expression))
                        {

                            this.candidates.Add(expression);

                        }

                        else
                        {

                            this.cannotBeEvaluated = true;

                        }

                    }

                    this.cannotBeEvaluated |= saveCannotBeEvaluated;

                }

                return expression;

            }

        }

    }
}
